package data

import (
	"database/sql"
	"errors"
	"fmt"
	"gitchina/kly_service/util"
	"strconv"
	"time"

	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"
)

type WaybillDto struct {
	Id                int     `orm:"pk;auto;column(id)"`
	CargoOwnerId      int     `orm:"column(cargo_owner_id)"`
	CarrierOwnerId    int     `orm:"column(carrier_owner_id)"`
	CarrierPubId      int     `orm:"column(carrier_pub_id)"`
	ShipmentTime      string  `orm:"column(shipment_time)"`
	StartArea         int     `orm:"column(start_area)"`
	StartAddr         string  `orm:"column(start_addr)"`
	EndArea           int     `orm:"column(end_area)"`
	EndAddr           string  `orm:"column(end_addr)"`
	SrcGeo            string  `orm:"column(src_geo)"`
	DestGeo           string  `orm:"column(dest_geo)"`
	SrcLocationTitle  string  `orm:"column(src_location_title)"`
	DestLocationTitle string  `orm:"column(dest_location_title)"`
	SrcLocation       string  `orm:"column(src_location)"`
	DestLocation      string  `orm:"column(dest_location)"`
	CargoName         string  `orm:"column(cargo_name)"`
	CargoType         int     `orm:"column(cargo_type)"`
	CargoBulk         float32 `orm:"column(cargo_bulk)"`
	CargoWeight       float32 `orm:"column(cargo_weight)"`
	CargoCount        int     `orm:"column(cargo_count)"`
	CargoValue        float32 `orm:"column(cargo_value)"`
	ArrivalTime       string  `orm:"column(arrival_time)"`
	PaymentTerm       int     `orm:"column(payment_term)"`
	Deposit           float32 `orm:"column(deposit)"`
	Price             float32 `orm:"column(price)"`
	Consignor         string  `orm:"column(consignor)"`
	Consignee         string  `orm:"column(consignee)"`
	ConsigneePhone    string  `orm:"column(consignee_phone)"`
	NeedInvoice       bool    `orm:"column(need_invoice)"`
	NeedInsurance     bool    `orm:"column(need_insurance)"`
	NeedOilCard       bool    `orm:"column(need_oil_card)"`
	OilCardNo         string  `orm:"column(oilcard_no)"`
	State             int     `orm:"column(state)"`
	Remark            string  `orm:"column(remark)"`
}

type GetWaybillListDto struct {
	Id           int     `orm:"column(id)"`
	ShipmentTime string  `orm:"column(shipment_time)"`
	StartArea    int     `orm:"column(start_area)"`
	EndArea      int     `orm:"column(end_area)"`
	Deposit      float32 `orm:"column(deposit)"`
	Price        float32 `orm:"column(price)"`
	CargoType    int     `orm:"column(cargo_type)"`
	CargoName    string  `orm:"column(cargo_name)"`
	PaymentTerm  int     `orm:"column(payment_term)"`
	State        int     `orm:"column(state)"`
}

type WaybillDetailDto struct {
	CargoOwner        string  `orm:"column(cargo_owner)"`
	CarrierOwner      string  `orm:"column(carrier_owner)"`
	StartArea         int     `orm:"column(start_area)"`
	StartAddr         string  `orm:"column(start_addr)"`
	EndArea           int     `orm:"column(end_area)"`
	EndAddr           string  `orm:"column(end_addr)"`
	SrcGeo            string  `orm:"column(src_geo)"`
	DestGeo           string  `orm:"column(dest_geo)"`
	SrcLocationTitle  string  `orm:"column(src_location_title)"`
	DestLocationTitle string  `orm:"column(dest_location_title)"`
	SrcLocation       string  `orm:"column(src_location)"`
	DestLocation      string  `orm:"column(dest_location)"`
	VehicleNo         string  `orm:"column(vehicle_no)"`
	VehicleType       int     `orm:"column(vehicle_type)"`
	VehicleLen        int     `orm:"column(vehicle_len)"`
	VehicleLoad       int     `orm:"column(vehicle_load)"`
	ShipmentTime      string  `orm:"column(shipment_time)"`
	ArrivalTime       string  `orm:"column(arrival_time)"`
	CarrierPubId      int     `orm:"column(carrier_pub_id)"`
	CargoName         string  `orm:"column(cargo_name)"`
	CargoType         int     `orm:"column(cargo_type)"`
	CargoBulk         float32 `orm:"column(cargo_bulk)"`
	CargoWeight       float32 `orm:"column(cargo_weight)"`
	CargoCount        int     `orm:"column(cargo_count)"`
	CargoValue        float32 `orm:"column(cargo_value)"`
	Deposit           float32 `orm:"column(deposit)"`
	Price             float32 `orm:"column(price)"`
	Consignor         string  `orm:"column(consignor)"`
	Consignee         string  `orm:"column(consignee)"`
	ConsigneePhone    string  `orm:"column(consignee_phone)"`
	Remark            string  `orm:"column(remark)"`
	CarrierOwnerId    int     `orm:"column(carrier_owner_id)"`
	CargoOwnerId      int     `orm:"column(cargo_owner_id)"`
	State             int     `orm:"column(state)"`
	NeedInvoice       bool    `orm:"column(need_invoice)"`
	NeedInsurance     bool    `orm:"column(need_insurance)"`
	NeedOilCard       bool    `orm:"column(need_oil_card)"`
	OilCardNo         string  `orm:"column(oilcard_no)"`
	PaymentTerm       int     `orm:"column(payment_term)"`
	CarrierOwnerPhone string  `orm:"column(carrier_owner_phone)"`
	OrderTime         string  `orm:"column(order_time)"`
	LineType          int
	CargoOwnerPhone   string
}

type LetterOfAuthorizationDto struct {
	CarrierOwner string `orm:"column(carrier_owner)"`
	IDCardNo     string `orm:"column(id_card_no)"`
	VehicleNo    string `orm:"column(vehicle_no)"`
	OrderTime    string `orm:"column(order_time)"`
}

type WaybillOwnerDto struct {
	CargoOwnerId   int    `orm:"column(cargo_owner_id)"`
	CarrierOwnerId int    `orm:"column(carrier_owner_id)"`
	Consignor      string `orm:"column(consignor)"`
	ArrivalTime    string `orm:"column(arrival_time)"`
	StartAddr      string `orm:"column(start_addr)"`
	EndAddr        string `orm:"column(end_addr)"`
	Consignee      string `orm:"column(consignee)"`
	ConsigneePhone string `orm:"column(consignee_phone)"`
}

type NotCompleteWaybillIds struct {
	WaybillId      int `orm:"column(id)"`
	CargoOwnerId   int `orm:"column(cargo_owner_id)"`
	CarrierOwnerId int `orm:"column(carrier_owner_id)"`
}

type ContractDto struct {
	Id        int    `orm:"column(id)"`
	MemberId  int    `orm:"column(member_id)"`
	WaybillId int    `orm:"column(waybill_id)"`
	TypeId    int    `orm:"column(type_id)"`
	Content   string `orm:"column(content)"`
}

type WaybillMessageDto struct {
	ID         int64  `orm:"pk;auto;column(id)"`
	OwnerID    int    `orm:"column(owner_id)"`
	WaybillID  int    `orm:"column(waybill_id)"`
	Text       string `orm:"column(text)"`
	Extension  string `orm:"column(extension)"`
	CreateTime string `orm:"column(create_time)"`
}

type WaybillScore struct {
	ID             int64  `orm:"pk;auto;column(id)"`
	CarrierOwnerID int    `orm:"column(carrier_owner_id)"`
	CommenterID    int    `orm:"column(commenter_id)"`
	WaybillID      int    `orm:"column(waybill_id)"`
	Score          int    `orm:"column(score)"`
	Comment        string `orm:"column(comment)"`
	Issued         bool   `orm:"column(is_issued)"`
	CreateTime     string `orm:"column(create_time)"`
}

type WaybillScoreWithName struct {
	NickName   string `orm:"column(nick_name)"`
	Score      int    `orm:"column(score)"`
	Comment    string `orm:"column(comment)"`
	CreateTime string `orm:"column(create_time)"`
}

func init() {
	orm.RegisterModel(new(WaybillDto))
	orm.RegisterModel(new(ContractDto))
	orm.RegisterModel(new(WaybillMessageDto))
	orm.RegisterModel(new(WaybillScore))
}

func (dto *WaybillDto) TableName() string {
	return "o_waybill"
}

func (dto *ContractDto) TableName() string {
	return "o_contract"
}

func (o *WaybillMessageDto) TableName() string {
	return "o_waybill_message"
}

func (o *WaybillScore) TableName() string {
	return "o_waybill_score"
}

func (repo OrderRepo) MakeWaybill(dto *WaybillDto) (id int64, err error) {
	var num int64
	var list orm.ParamsList
	o := orm.NewOrm()
	if err = o.Begin(); err == nil {
		if num, err = o.Raw(SQL_Order_Ensure_Only_One_Waybill, dto.CargoOwnerId, dto.CarrierPubId).ValuesFlat(&list); num >= 1 || err != nil {
			o.Rollback()
			if num >= 1 {
				id, err = strconv.ParseInt(list[0].(string), 10, 32)
			}
		} else {
			if id, err = o.Insert(dto); err != nil {
				o.Rollback()
			} else {
				_, err = o.Raw(SQL_Order_Freeze_Carrier_Pub, dto.CarrierPubId).Exec()
				if err != nil {
					o.Rollback()
				} else {
					o.Commit()
				}
			}
		}
	}
	return
}

func (repo OrderRepo) GetWaybillList(id, pageNo, pageSize int, queryState, mbrType string) ([]GetWaybillListDto, int, error) {
	var sql string
	var rowCountSql string
	var num int64
	var rowCount int
	var err error
	var flat orm.ParamsList
	var waybillList []GetWaybillListDto
	o := orm.NewOrm()

	var stateWhere string
	if queryState == "1" {
		stateWhere = " AND state < 7"
	} else if queryState == "2" {
		stateWhere = " AND state >= 7"
	}

	if mbrType == "1" || mbrType == "3" {
		sql = fmt.Sprintf(SQL_Order_Get_Waybill_By_Cargo_Owner, stateWhere, (pageNo-1)*pageSize, pageSize)
		rowCountSql = fmt.Sprintf(SQL_Order_Get_Waybill_By_Cargo_Owner_Count, stateWhere)
	} else if mbrType == "2" || mbrType == "4" {
		sql = fmt.Sprintf(SQL_Order_Get_Waybill_By_Carrier_Owner, stateWhere, (pageNo-1)*pageSize, pageSize)
		rowCountSql = fmt.Sprintf(SQL_Order_Get_Waybill_By_Carrier_Owner_Count, stateWhere)
	} else {
		return nil, 0, errors.New("invalid member type")
	}
	if num, err = o.Raw(sql, id).QueryRows(&waybillList); num >= 1 {
		if num, err = o.Raw(rowCountSql, id).ValuesFlat(&flat); num >= 1 {
			rowCount, err = strconv.Atoi(flat[0].(string))
			util.GetLogger().Debug("[data-GetWaybillList] - debug:rowcount::%s", flat[0])
		}
	}
	return waybillList, rowCount, err
}

func (repo OrderRepo) GetWaybillDetail(id int) (dto WaybillDetailDto, err error) {
	var mbrType string
	var cargoOwner CargoOwnerGetDto
	var logiCorp LogisticsCorpDetailDto
	o := orm.NewOrm()
	if err = o.Raw(SQL_Order_Get_Waybill_Detail, id).QueryRow(&dto); err == nil {
		var mbrRepo MemberRepo
		carrierType, err := mbrRepo.GetMemberType(dto.CarrierOwnerId)
		if err == nil {
			if carrierType == "2" {
				dto.LineType = 1
			} else {
				dto.LineType = 2
			}
		}

		if mbrType, err = mbrRepo.GetMemberType(dto.CargoOwnerId); err == nil {
			if mbrType == "1" {
				if cargoOwner, err = mbrRepo.GetCargoOwner(dto.CargoOwnerId); err == nil {
					if cargoOwner.IsCorp {
						if len(cargoOwner.CorpName) != 0 {
							dto.CargoOwner = cargoOwner.CorpName
						} else {
							dto.CargoOwner = cargoOwner.Name
						}
					} else {
						dto.CargoOwner = cargoOwner.Name
					}
					dto.CargoOwnerPhone = cargoOwner.Phone
				}
			} else if mbrType == "3" {
				var logiRepo LogisticsRepo
				if logiCorp, err = logiRepo.GetLogisticsCorpDetail(dto.CargoOwnerId); err == nil {
					dto.CargoOwner = logiCorp.CorpName
					dto.CargoOwnerPhone = logiCorp.OwnerPhone
				}
			}
		}
	}
	return dto, err
}

func (repo OrderRepo) GetWaybillState(waybillId int) (state int, updTime string, err error) {
	var num int64
	var list orm.ParamsList
	state = -1
	o := orm.NewOrm()
	if num, err = o.Raw(SQL_Order_Get_Waybill_State, waybillId).ValuesFlat(&list); num > 0 {
		state, err = strconv.Atoi(list[0].(string))
		updTime = list[1].(string)
	}
	return
}

func (repo OrderRepo) UpdateWaybill(dto *WaybillDto) (err error) {
	var waybillState int
	var ok bool
	o := orm.NewOrm()
	q := o.QueryTable("o_waybill").
		Filter("Id", dto.Id).
		Filter("CargoOwnerId", dto.CargoOwnerId).
		Filter("State__in", 0, 1, 2, 3, 4)
	params := orm.Params{
		"ShipmentTime":      dto.ShipmentTime,
		"StartArea":         dto.StartArea,
		"StartAddr":         dto.StartAddr,
		"EndArea":           dto.EndArea,
		"EndAddr":           dto.EndAddr,
		"SrcGeo":            dto.SrcGeo,
		"DestGeo":           dto.DestGeo,
		"SrcLocationTitle":  dto.SrcLocationTitle,
		"DestLocationTitle": dto.DestLocationTitle,
		"SrcLocation":       dto.SrcLocation,
		"DestLocation":      dto.DestLocation,
		"CargoName":         dto.CargoName,
		"CargoType":         dto.CargoType,
		"CargoBulk":         dto.CargoBulk,
		"CargoWeight":       dto.CargoWeight,
		"CargoCount":        dto.CargoCount,
		"CargoValue":        dto.CargoValue,
		"ArrivalTime":       dto.ArrivalTime,
		"Consignor":         dto.Consignor,
		"PaymentTerm":       dto.PaymentTerm,
		"Remark":            dto.Remark,
		"Consignee":         dto.Consignee,
		"ConsigneePhone":    dto.ConsigneePhone,
		"NeedOilCard":       dto.NeedOilCard,
		"NeedInvoice":       dto.NeedInvoice,
		"NeedInsurance":     dto.NeedInsurance,
		"OilCardNo":         dto.OilCardNo,
	}

	if waybillState, _, err = repo.GetWaybillState(dto.Id); err == nil {
		var payRepo PayRepo
		if waybillState == 0 {
			//是否当前为支付状态或支付成功状态
			if ok, err = payRepo.CheckSuccPaymentRecord(dto.Id, 0); err == nil {
				if ok {
					params["Price"] = dto.Price
					_, err = q.Update(params)
				} else {
					//如果没有支付失败或者支付取消，可以更新订金金额
					params["Deposit"] = dto.Deposit
					params["Price"] = dto.Price
					_, err = q.Update(params)
				}
			}
		} else if waybillState == 1 || waybillState == 2 {
			params["Price"] = dto.Price
			_, err = q.Update(params)
		} else if waybillState == 3 {
			if ok, err = payRepo.CheckSuccPaymentRecord(dto.Id, 1); err == nil {
				if ok {
					_, err = q.Update(params)
				} else {
					params["Price"] = dto.Price
					_, err = q.Update(params)
				}
			}

		} else if waybillState == 4 {
			_, err = q.Update(params)
		}
	}
	return err
}

func (repo OrderRepo) UpdateWaybillState(waybillId, mbrId, state int, parmas ...interface{}) (err error) {
	var sql string
	o := orm.NewOrm()
	switch state {
	case 1:
		sql = SQL_Order_Switch_Deposit_Paid
	case 2:
		sql = SQL_Order_Switch_OnTheWay
	case 4:
		sql = SQL_Order_Switch_OnContract_Wait2Confirm
	case 5:
		sql = SQL_Order_Switch_OnShipment
	case 6:
		sql = SQL_Order_Switch_OnArrival
	}
	if sql != "" {
		_, err = o.Raw(sql, waybillId, mbrId, parmas).Exec()
	} else {
		err = errors.New("invalid order state")
	}

	return err
}

func (repo OrderRepo) UpdateOfflineWaybillState(waybillID, cargoOwnerID int) error {
	o := orm.NewOrm()
	_, err := o.Raw(SQL_Order_Switch_OnContract_Offline, waybillID, cargoOwnerID).Exec()
	return err
}

func (repo OrderRepo) UpdateWaybillStateWithParams(waybillId, mbrId, paymentTerm int, needInvoice, needInsurance bool, invoiceDto InvoiceDto) (err error) {
	o := orm.NewOrm()
	if err = o.Begin(); err == nil {
		if needInvoice {
			_, _, err = o.ReadOrCreate(&invoiceDto, "WaybillId")
		}
		if err == nil {
			if _, err = o.Raw(SQL_Order_Switch_OnContract_Wait2Pay, needInvoice, needInsurance, paymentTerm, waybillId, mbrId).Exec(); err == nil {
				o.Commit()
			} else {
				o.Rollback()
			}
		} else {
			o.Rollback()
		}
	} else {
		o.Rollback()
	}
	return err
}

func (repo OrderRepo) UpdateOilCard(waybillId, carrierOwnerId int, needOilCard bool, oilCardNo string) error {
	o := orm.NewOrm()
	_, err := o.Raw(SQL_Logis_Use_OildCard, needOilCard, oilCardNo, waybillId, carrierOwnerId).Exec()
	return err
}

func (repo OrderRepo) CompleteWaybill(waybillId, cargoOwnerId int) (err error) {
	var num int64
	var list orm.ParamsList
	var result sql.Result
	var rowsAffected int64
	o := orm.NewOrm()
	if num, err = o.Raw(SQL_Order_Get_Pub_Id_By_Waybill_Id, waybillId).ValuesFlat(&list); num >= 1 {
		if err = o.Begin(); err == nil {
			if result, err = o.Raw(SQL_Order_Switch_Complete, waybillId, cargoOwnerId).Exec(); err == nil {
				if rowsAffected, err = result.RowsAffected(); rowsAffected >= 1 {
					if _, err = o.Raw(fmt.Sprintf(SQL_Order_Set_Carrier_Pub, 2), list[0].(string)).Exec(); err == nil {
						o.Raw(SQL_Order_Invoice_Complete, waybillId, cargoOwnerId).Exec()
						o.Commit()
					} else {
						o.Rollback()
					}
				} else {
					o.Rollback()
				}
			} else {
				o.Rollback()
			}
		} else {
			o.Rollback()
		}

		if err == nil {
			repo.AddWaybillScore(waybillId, 5, "系统默认评价", false)
		}
	}
	return
}

func (repo OrderRepo) CompleteInvoice(memberID, waybillID int) error {
	o := orm.NewOrm()
	_, err := o.Raw(SQL_Order_Invoice_Complete, waybillID, memberID).Exec()
	return err
}

func (repo OrderRepo) CancelWaybill(waybillId, mbrId int) (err error) {
	var num int64
	var list orm.ParamsList
	var result sql.Result
	var rowsAffected int64
	o := orm.NewOrm()
	if num, err = o.Raw(SQL_Order_Get_Pub_Id_By_Waybill_Id, waybillId).ValuesFlat(&list); num >= 1 {
		if err = o.Begin(); err == nil {
			if result, err = o.Raw(SQL_Order_Switch_Cancel, waybillId, mbrId, mbrId).Exec(); err == nil {
				if rowsAffected, err = result.RowsAffected(); rowsAffected >= 1 {
					if _, err = o.Raw(fmt.Sprintf(SQL_Order_Set_Carrier_Pub, 3), list[0].(string)).Exec(); err == nil {
						o.Commit()
					} else {
						o.Rollback()
					}
				} else {
					o.Rollback()
				}
			} else {
				o.Rollback()
			}
		} else {
			o.Rollback()
		}
	}

	return err
}

func (repo OrderRepo) GetWaybillOwners(waybillId int) (dto WaybillOwnerDto, err error) {
	o := orm.NewOrm()
	err = o.Raw(SQL_Order_Get_Waybill_Owner, waybillId).QueryRow(&dto)
	return
}

func (repo OrderRepo) GetLetterOfAuthorization(waybillId int) (dto LetterOfAuthorizationDto, err error) {
	o := orm.NewOrm()
	err = o.Raw(SQL_Order_Get_Letter_Of_Authorization, waybillId).QueryRow(&dto)
	return
}

func (repo OrderRepo) GetNotCompleteWaybills() (ids []NotCompleteWaybillIds, err error) {
	o := orm.NewOrm()
	_, err = o.Raw(fmt.Sprintf(SQL_Order_Get_Not_Complete_Waybills, time.Now().Add(-3*24*time.Hour).Format("2006-01-02 15:04:05"))).QueryRows(&ids)

	return
}

func (repo OrderRepo) SaveContract(c ContractDto) error {
	o := orm.NewOrm()
	_, err := o.Insert(&c)
	return err
}

func (repo OrderRepo) GetContract(memberId, waybillId, typeId int) (content string, err error) {
	var contract ContractDto
	o := orm.NewOrm()
	if err := o.QueryTable("o_contract").
		Filter("WaybillId", waybillId).
		Filter("MemberId", memberId).
		Filter("TypeId", typeId).
		One(&contract); err == nil {
		content = contract.Content
	}
	return
}

func (repo OrderRepo) AddMessage(m WaybillMessageDto) error {
	o := orm.NewOrm()
	m.CreateTime = time.Now().Format("2006-01-02 15:04:05")
	_, err := o.Insert(&m)
	return err
}

func (repo OrderRepo) GetWaybillMessage(ownerID int) (list []WaybillMessageDto, err error) {
	o := orm.NewOrm()
	_, err = o.QueryTable(new(WaybillMessageDto)).Filter("OwnerID", ownerID).OrderBy("-id").All(&list)
	return
}

func (repo OrderRepo) AddWaybillScore(waybillID, score int, comment string, issued bool) error {
	var err error
	var owners WaybillOwnerDto
	o := orm.NewOrm()

	if owners, err = repo.GetWaybillOwners(waybillID); err == nil {
		waybillScore := WaybillScore{
			CarrierOwnerID: owners.CarrierOwnerId,
			CommenterID:    owners.CargoOwnerId,
			WaybillID:      waybillID,
			Comment:        comment,
			Score:          score,
			Issued:         issued,
			CreateTime:     time.Now().Format("2006-01-02 15:04:05"),
		}
		if err = o.Begin(); err == nil {
			if _, err = o.Insert(&waybillScore); err == nil {
				_, err = o.Raw(fmt.Sprintf(SQL_Mbr_Score_Update, score), owners.CarrierOwnerId).Exec()
			}
		}
		if err != nil {
			o.Rollback()
		} else {
			o.Commit()
		}
	}

	return err
}

func (repo OrderRepo) UpdateWaybillScore(waybillID, score int, comment string) error {
	var err error
	var count int64
	var result sql.Result
	var owners WaybillOwnerDto
	o := orm.NewOrm()
	if owners, err = repo.GetWaybillOwners(waybillID); err == nil {
		if count, err = o.QueryTable(new(WaybillScore)).
			Filter("CommenterID", owners.CargoOwnerId).
			Filter("WaybillID", waybillID).Count(); err == nil {
			if count == 1 {
				if err = o.Begin(); err == nil {
					if result, err = o.Raw(SQL_Order_Score_Update, score, comment, waybillID, owners.CargoOwnerId).Exec(); err == nil {
						if count, err = result.RowsAffected(); count == 1 {
							_, err = o.Raw(fmt.Sprintf(SQL_Mbr_Score_Update2, score), owners.CarrierOwnerId).Exec()
						}
					}
				}
				if err != nil {
					o.Rollback()
				} else {
					o.Commit()
				}
			} else {
				err = repo.AddWaybillScore(waybillID, score, comment, true)
			}
		}
	}
	return err
}

func (repo OrderRepo) GetWaybillScoreList(carrierOwnerID int) ([]WaybillScoreWithName, error) {
	var list []WaybillScoreWithName
	o := orm.NewOrm()
	_, err := o.Raw(SQL_Order_Get_Score_List, carrierOwnerID).QueryRows(&list)
	return list, err
}
